importScripts("./libshared/earcut.js");
importScripts("./libshared/Point.js");
importScripts("./libshared/Pbf.js");
importScripts("./libshared/MVTData.js");
importScripts("./libshared/MVTFeature.js");
importScripts("./libshared/MVTLayer.js");

importScripts("./core/mathengine.js");
importScripts("./core/vector2.js");
importScripts("./core/vector3.js");
importScripts("./core/vector4.js");
importScripts("./core/matrix3f.js");
importScripts("./core/matrix4f.js");
importScripts("./core/matrix4d.js");
importScripts("./core/size.js");
importScripts("./core/bounds.js");
importScripts("./core/projection.js");
importScripts("./core/triangulator.js");
importScripts("./core/coordsys.js");
importScripts("./core/tilecoordsys.js");

importScripts("./buildingstessellator.js");
importScripts("./buildingtessellator.js");
importScripts("./buildingstessellator_line.js");
importScripts("./buildingtessellator_line.js");
importScripts("./colorbuildingtessellator.js");
importScripts("./terraintessellator.js");
importScripts("./linestessellator.js");
importScripts("./linetessellator.js");

function VectorTileProcessor() {
    this.coordSys = new TileCoordSys();
}

Object.assign(VectorTileProcessor.prototype, {

	process: function (message) {
        this.coordSys.setEPSG(message.epsg);
        this.coordSys.setTileBounds(message.tileBounds);

        if (message.fun == "processBuildingTile") {
            this.loadBuildingTile(message, this.response);
        } else if (message.fun == "processTerrainTile") {
            this.loadTerrainTile(message, this.response);
        } else if (message.fun == "processLineTile") {
            this.loadLineTile(message, this.response);
        }
        else if (message.fun == "processLineTile2") {
            this.loadLineTile2(message, this.response);
        }
        else if (message.fun == 'process3DTile') {
            this.load3DTiles(message, this.response);
        }
	},


    loadTerrainTile: function (message) {
        var _this = this;

        var url = this.getRequestURL(message.url, message.z, message.y, message.x);
        var xhr = new XMLHttpRequest();
        xhr.timeout = 0;
        xhr.withCredentials = false;
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {

            if (this.status===200 && this.response.byteLength > 256  ) {
                _this.processTerrainTile(message, this.response);
            }
            else{

                var transferableObjects = [];
                message.name = "status code:" + this.status;
                postMessage({result:message, buffer:transferableObjects}, transferableObjects);

            }
        }

        xhr.onerror = function(event) {
            var transferableObjects = [];
            message.name = "error" + event.message;
            postMessage({result:message, buffer:transferableObjects}, transferableObjects);
        }

        xhr.ontimeout = function(event) {
            var transferableObjects = [];
            message.name = "timeout" + event.message;
            postMessage({result:message, buffer:transferableObjects}, transferableObjects);
        }

        xhr.open("GET", url, false);
        xhr.send(null);
	},

    createTerrainTessellator: function (terrainType) {
        return new TerrainTessellator();
    },

    processTerrainTile: function (message, response) {

	    var tessellator = this.createTerrainTessellator(message.type);
        var tilelayer = this.parserTerrainLayer(message, response, tessellator);

        var transferableObjects = [];
        if (tilelayer != null) {
            message.name = tilelayer.name;
            message.layouts = tessellator.toTransferableObjects(tilelayer.vertexBuffer, tilelayer.indexBuffer, transferableObjects, message.flat);
        }

        postMessage({result:message, buffer:transferableObjects}, transferableObjects);
    },

    parserTerrainLayer: function (message, response, tessellator) {

        var rcBounds = this.coordSys.computeTileBounds(message.z, message.y, message.x);
        var center = rcBounds.getCenter();
        var local = new Vector2(center.x, center.y);
        this.coordSys.worldToGeo(center);

        var leftTop = new Vector2(rcBounds.left, rcBounds.top);
        message.radius = leftTop.sub(local).length();
        message.center = center;

        var tilelayer = {};
        tilelayer.name = message.name;
        tilelayer.vertexBuffer = [];
        tilelayer.indexBuffer = [];

        var length = response.byteLength;
        var tileSize = 64*64; //��Ƭ��С��67X67��
        var terrains = null;
        if(Math.floor(length/tileSize) === 2) {//16λ���ݸ�ʽ
            terrains = new Int16Array(response);
        }
        else if(Math.floor(length / tileSize) === 4){ //32λ���ݸ�ʽ
            terrains = new Float32Array(response);
        }

        tessellator.process(this.coordSys, tilelayer.vertexBuffer, tilelayer.indexBuffer, message, terrains, local);

        return tilelayer;
    },


    loadBuildingTile: function (message) {
        var _this = this;

        var url = this.getRequestURL(message.url, message.z, message.y, message.x);
        var xhr = new XMLHttpRequest();
        xhr.timeout = 0;
        xhr.withCredentials = false;
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {
            if (this.status===200 && this.response.byteLength > 256 ) {
                _this.processBuildingTile(message, this.response);
            }
        }
        xhr.open("GET", url, true);

        //test time out and try request again 3 times.
        // var timeoutID = null;
        // var tryTimes = 3;
        // xhr.ontimeout = function (e) {
        //     // XMLHttpRequest 超时。在此做某事。
        //     if(timeoutID)
        //     {
        //         clearTimeout(timeoutID);
        //     }
        //     tryTimes--;
        //     let thisXhr = this;
        //     thisXhr.timeout += 1000;

        //     console.log("timeout,try %d times after:%d",tryTimes,thisXhr.timeout);

        //     if(tryTimes>=0){

        //         //过5秒再发送一次请求
        //         timeoutID = setTimeout(function(){
        //             thisXhr.open("GET", url, true);
        //             thisXhr.send(null);
        //         },thisXhr.timeout);
        //     }

        // };

        xhr.send(null);
    },

    createBuildingTessellator: function (buildingType) {
        if (buildingType == "ColorBuilding") {
            return new ColorBuildingTessellator();
        }
        //return new BuildingTessellator();
        return new BuildingTessellator_line();
    },

    processBuildingTile: function (message, response) {
        var tile = new MVTData(new Pbf(response));

        var rcBounds = this.coordSys.computeTileBounds(message.z, message.y, message.x);
        var center = rcBounds.getCenter();
        var local = new Vector2(center.x, center.y);
        this.coordSys.worldToGeo(center);

        var leftTop = new Vector2(rcBounds.left, rcBounds.top);
        message.radius = leftTop.sub(local).length();
        message.center = center;

        var tessellator = this.createBuildingTessellator(message.type);

        var tilelayer = null;
        var layer = tile.layers[message.name];
        if (layer != null) {
            tilelayer = this.parserBuildingLayer(message, center, local, layer, tessellator);
        }

        //var transferableObjects = [];
        var transferableObjects = {};
        transferableObjects.face = [];
        transferableObjects.line = null;
        if ( message.isCreateLine )
            transferableObjects.line = [];
        if (tilelayer != null) {
            message.name = tilelayer.name;
            //message.layouts = tessellator.toTransferableObjects(tilelayer.vertexBuffers, tilelayer.indexBuffers, local, transferableObjects);
            message.layouts = tessellator.toTransferableObjects(tilelayer.vertexBuffers, tilelayer.indexBuffers, tilelayer.vertexBuffers_line, local, transferableObjects);
        }

        postMessage({result:message, buffer:transferableObjects}, transferableObjects);
    },

    parserBuildingLayer: function (message, center, local, layer, tessellator) {
        var tilelayer = {};
        tilelayer.name = layer.name;
        tilelayer.vertexBuffers = [];
        tilelayer.indexBuffers = [];
        if ( message.isCreateLine )
            tilelayer.vertexBuffers_line = [];
        else
            tilelayer.vertexBuffers_line = null;

        var heightKey = message.heightField;
        var attrKey = message.attributeField;
        var attrStageKey = message.attrStageField;
        var idKey = message.idField;
        var HighTerrainKey = message.HighTerrain;
        for (var i = 0; i < layer.length; i++) {
            var feature = layer.feature(i);
            if (feature != null) {
                var height = 1.0;
                if ( feature.properties[heightKey] != undefined )
                    height = feature.properties[heightKey];
                var attr = height;
                var attrStage = height;
                if ( attrKey !== undefined && feature.properties[attrKey] != undefined )
                    attr = feature.properties[attrKey];
                if ( attrStageKey !== undefined && feature.properties[attrStageKey] != undefined  )
                    attrStage = feature.properties[attrStageKey];
                var id = 0;
                if ( idKey !== undefined && feature.properties[idKey] != undefined  )
                    id = feature.properties[idKey];
                var HighTerrain=0.0;
                if(feature.properties[HighTerrainKey] != undefined){
                    HighTerrain=feature.properties[HighTerrainKey];
                }

                var rings = classifyRings(feature.loadGeometry());
                for (var j = 0; j < rings.length; j++) {
                    var ring = rings[j];
                    for (var k = 0; k < ring.length; k++) {
                        //this.tessBuilding(message, local, ring[k], height, attr, attrStage, id, tessellator, tilelayer.vertexBuffers, tilelayer.indexBuffers);
                        this.tessBuilding_line(message, local, ring[k], height, attr, attrStage, id, tessellator, tilelayer.vertexBuffers, tilelayer.indexBuffers, tilelayer.vertexBuffers_line,HighTerrain);
                    }
                }
            }
        }

        return tilelayer;
    },

    tessBuilding: function (message, local, coords, height, attr, attrStage, id, tessellator, vertexBuffers, indexBuffers) {

        for (var i = 0; i < coords.length; i++) {
            this.coordSys.tileToWorld(message.z, message.y, message.x, coords[i]);
        }

        var tessellators = new BuildingsTessellator();
        tessellators.process(tessellator, vertexBuffers, indexBuffers, coords, height, attr, attrStage, local, 1.0, id);
    },

    tessBuilding_line: function (message, local, coords, height, attr, attrStage, id, tessellator, vertexBuffers, indexBuffers, vertexBuffers_line,HighTerrain) {

        for (var i = 0; i < coords.length; i++) {
            this.coordSys.tileToWorld(message.z, message.y, message.x, coords[i]);
        }

        var tessellators = new BuildingsTessellator_line();
        tessellators.process(tessellator, vertexBuffers, indexBuffers, coords, height, attr, attrStage, local, 1.0, id, vertexBuffers_line,HighTerrain);
    },

    loadLineTile: function (message) {
        var _this = this;

        var url = this.getRequestURL(message.url, message.z, message.y, message.x);
        var xhr = new XMLHttpRequest();
        xhr.timeout = 0;
        xhr.withCredentials = false;
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {
            if (this.status == 200 && this.response.byteLength > 30) {
                _this.processLineTile(message, this.response);
            }
        }
        xhr.open("GET", url, false);
        xhr.send(null);
    },

    loadLineTile2: function (message) {
        var _this = this;

        var url = this.getRequestURL(message.url, message.z, message.y, message.x);
        var xhr = new XMLHttpRequest();
        xhr.timeout = 0;
        xhr.withCredentials = false;
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {
            if (this.status == 200 && this.response.byteLength > 30) {
                _this.processLineTile2(message, this.response);
            }
        }
        xhr.open("GET", url, false);
        xhr.send(null);
    },

    createLineTessellator: function (LineType) {
        return new LineTessellator();
    },

    processLineTile: function (message, response) {
        var tile = new MVTData(new Pbf(response));

        var rcBounds = this.coordSys.computeTileBounds(message.z, message.y, message.x);
        var center = rcBounds.getCenter();
        var local = new Vector2(center.x, center.y);
        this.coordSys.worldToGeo(center);

        var leftTop = new Vector2(rcBounds.left, rcBounds.top);
        message.radius = leftTop.sub(local).length();
        message.center = center;

        var tessellator = this.createLineTessellator(message.type);

        var tilelayer = null;
        var layer = tile.layers[message.name];
        if (layer != null) {

            tilelayer = this.parserLineLayer(message, center, local, layer, tessellator);

        }

        var transferableObjects = [];
        if (tilelayer != null) {
            message.name = tilelayer.name;
            message.layouts = tessellator.toTransferableObjects(tilelayer.vertexBuffers, tilelayer.indexBuffers, transferableObjects);
        }

        postMessage({result:message, buffer:transferableObjects}, transferableObjects);
    },

    parserLineLayer: function (message, center, local, layer, tessellator) {
        var tilelayer = {};
        tilelayer.name = layer.name;
        tilelayer.vertexBuffers = [];
        tilelayer.indexBuffers = [];

        for (var i = 0; i < layer.length; i++) {
            var feature = layer.feature(i);
            if (feature != null) {
                var rings = classifyRings(feature.loadGeometry());
                for (var j = 0; j < rings.length; j++) {
                    var ring = rings[j];
                    for (var k = 0; k < ring.length; k++) {
                        this.tessLine(message, local, ring[k], tessellator, tilelayer.vertexBuffers, tilelayer.indexBuffers);
                    }
                }
            }
        }

        return tilelayer;
    },

    tessLine: function (message, local, coords, tessellator, vertexBuffers, indexBuffers) {

        var times = [];
        let sumLength = 0;
        for (var i = 0; i < coords.length; i++) {
            if(!message.dynamic){
                times.push(0);
            }
            else
            {
                if(i===0){
                    times.push(0);
                }
                else {
                    let length = Math.sqrt((coords[i].x-coords[i-1].x)*(coords[i].x-coords[i-1].x)+(coords[i].y-coords[i-1].y)*(coords[i].y-coords[i-1].y));
                    sumLength += length;
                    times.push(sumLength);
                }
            }

            this.coordSys.tileToWorld(message.z, message.y, message.x, coords[i]);
        }
        // for(var i=0;i<times.length;i++){
        //     times[i] = times[i]*2000/sumLength;
        // }


        var tessellators = new LinesTessellator();
        tessellators.process(tessellator, vertexBuffers, indexBuffers, coords, times, local, 1.0);
    },

    //try code:
    processLineTile2: function (message, response) {
        var tile = new MVTData(new Pbf(response));

        var rcBounds = this.coordSys.computeTileBounds(message.z, message.y, message.x);
        var center = rcBounds.getCenter();
        var local = new Vector2(center.x, center.y);
        this.coordSys.worldToGeo(center);

        var leftTop = new Vector2(rcBounds.left, rcBounds.top);
        message.radius = leftTop.sub(local).length();
        message.center = center;

        var tessellator = this.createLineTessellator(message.type);

        var geoJsonData = null;
        var layer = tile.layers[message.name];
        if (layer != null) {

            geoJsonData = this.parserLineLayer2(message, center, local, layer, tessellator);
        }


        if (geoJsonData != null) {
            postMessage({result:message, buffer:geoJsonData}, geoJsonData);
        }


    },

    parserLineLayer2: function (message, center, local, layer, tessellator) {

	    var tilelayer = {};
        tilelayer.name = layer.name;
        //tilelayer.vertexBuffers = [];
        //tilelayer.indexBuffers = [];
        tilelayer.type = "FeatureCollection";
        tilelayer.features = [];

        for (var i = 0; i < layer.length; i++) {
            var feature = layer.feature(i);

            var geoFeature = {type:"feature",geometry:{type:"MultiLineString"}};
            geoFeature.properties = feature.properties;
            geoFeature.geometry.coordinates = [];

            if (feature != null) {
                var rings = classifyRings(feature.loadGeometry());

                for (var j = 0; j < rings.length; j++) {
                    var ring = rings[j];
                    var geoRing = [];

                    for (var k = 0; k < ring.length; k++) {
                        this.tessLine2(message, local, ring[k]);
                        var coords = [];
                        for(var l=0;l<ring[k].length;l++){
                            var pt = [ring[k][l].x,ring[k][l].y];
                            coords.push(pt);
                        }
                        geoRing.push(coords);
                    }
                    geoFeature.geometry.coordinates.push(geoRing);
                }
                tilelayer.features.push(geoFeature);
                //geoFeature.geometry.coordinates.push(geoRing);
            }
            //console.log(geoFeature);
        }

        return tilelayer;
    },

    tessLine2: function (message, local, coords) {

        for (var i = 0; i < coords.length; i++) {
            this.coordSys.tileToWorld(message.z, message.y, message.x, coords[i]);
            this.coordSys.worldToGeo(coords[i]);
        }

    },


    getRequestURL: function (url, z, y, x) {
        url = url.replace(/{z}/, z);
        url = url.replace(/{y}/, y);
        url = url.replace(/{x}/, x);
        return url;
    },

    load3DTiles: function (message) {
        var _this = this;

        var url = message.url;
        var xhr = new XMLHttpRequest();
        xhr.timeout = 0;
        xhr.withCredentials = false;
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {
            if (this.status===200 && this.response.byteLength > 256 ) {
                _this.process3DTiles(message, this.response);
            }
        }
        xhr.open("GET", url, true);
        xhr.send(null);
    },

    create3DTilesTessellator: function (buildingType) {
        // if (buildingType == "ColorBuilding") {
        //     return new ColorBuildingTessellator();
        // }
        return new tiles3DTessellator();
    },

    process3DTiles: function (message, response) {
        // var tile = new MVTData(new Pbf(response));
        //
        // var rcBounds = this.coordSys.computeTileBounds(message.z, message.y, message.x);
        // var center = rcBounds.getCenter();
        // var local = new Vector2(center.x, center.y);
        // this.coordSys.worldToGeo(center);
        //
        // var leftTop = new Vector2(rcBounds.left, rcBounds.top);
        // message.radius = leftTop.sub(local).length();
        // message.center = center;
        //
        // var tessellator = this.createBuildingTessellator(message.type);
        //
        // var tilelayer = null;
        // var layer = tile.layers[message.name];
        // if (layer != null) {
        //     tilelayer = this.parserBuildingLayer(message, center, local, layer, tessellator);
        // }
        //
        // var transferableObjects = [];
        // if (tilelayer != null) {
        //     message.name = tilelayer.name;
        //     message.layouts = tessellator.toTransferableObjects(tilelayer.vertexBuffers, tilelayer.indexBuffers, local, transferableObjects);
        // }

        // postMessage({result:message, buffer:transferableObjects}, transferableObjects);
    },

    parser3DTilesLayer: function (message, center, local, layer, tessellator) {
        // var tilelayer = {};
        // tilelayer.name = layer.name;
        // tilelayer.vertexBuffers = [];
        // tilelayer.indexBuffers = [];
        //
        // var heightKey = message.heightField;
        // var attrKey = message.attributeField;
        // for (var i = 0; i < layer.length; i++) {
        //     var feature = layer.feature(i);
        //     if (feature != null) {
        //         var height = feature.properties[heightKey] / 5000.0;
        //         var attr = Math.random()*20;
        //         if ( attrKey !== undefined )
        //             attr = feature.properties[attrKey];
        //         var rings = classifyRings(feature.loadGeometry());
        //         for (var j = 0; j < rings.length; j++) {
        //             var ring = rings[j];
        //             for (var k = 0; k < ring.length; k++) {
        //                 this.tessBuilding(message, local, ring[k], height, attr, tessellator, tilelayer.vertexBuffers, tilelayer.indexBuffers);
        //             }
        //         }
        //     }
        // }
        //
        // return tilelayer;
    },

    tess3DTile: function (message, local, coords, height, attr, tessellator, vertexBuffers, indexBuffers) {

        for (var i = 0; i < coords.length; i++) {
            this.coordSys.tileToWorld(message.z, message.y, message.x, coords[i]);
        }

        var tessellators = new Tiles3DTessellator();
        tessellators.process(tessellator, vertexBuffers, indexBuffers, coords, height, attr, local, 1.0);
    }

} );

